
// project      : soapi-notify 
// purpose      : console and growl notifications of new Stack Exchange questions
// source       : http://soapi.info/tools/notify.aspx
// announcement : http://stackapps.com/questions/820
// website      : http://soapi.info/tools/notify.aspx
// license      : MIT/X11 - Copyright (c) 2010 Sky Sanders - See License.txt
// inspiration  : http://stackapps.com/questions/817/senotify-se-new-question-notification
// 
//  
using System;
using System.Diagnostics;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Growl.CoreLibrary;
using Growl.DisplayStyle;
using Soapi.Notify.Displays.Default.Properties;

namespace Soapi.Notify.Displays.Default
{
    public partial class SoapiWindow : NotificationWindow
    {
        private const string TitleParsingPattern =
            @"\((?<votes>-?\d+)\|(?<answers>\d+)(?<accepted>\+?)\|(?<views>\d+)\)\s+(?<title>.*)";

        private readonly Color _answerAcceptedForeColor = Color.FromArgb(0xE1, 0xE8, 0x18);
        private readonly Color _answerNotAcceptedForeColor = Color.FromArgb(0xFF, 0xFF, 0xFF);
        private readonly Color _answersHasAnswersBackgroundColor = Color.FromArgb(0x75, 0x84, 0x5C);
        private readonly Color _answersHasNoAnswersBackgroundColor = Color.FromArgb(0x9A, 0x44, 0x44);
        private readonly Color _tagBackgroundColor = Color.FromArgb(224, 234, 241);
        private readonly Color _tagForeColor = Color.FromArgb(93, 109, 176);
        private int _bottomYLocation;
        private int _leftXLocation;
        private SoapiDisplay.Location _location = SoapiDisplay.Location.TopRight;
        private string _questionUrl;
        private int _rightXLocation;
        private string _tags;
        private string _tmpTags;
        private int _topYLocation;

        public SoapiWindow()
        {
            InitializeComponent();

            BackColor = Color.White;
            ForeColor = Color.FromArgb(51, 51, 51);

            PauseWhenMouseOver = true;

            AfterLoad += SoapiWindowAfterLoad;

            Animator = new FadeAnimator(this);

            HookUpClickEvents2(this);

            SetAutoCloseInterval(4000);
        }

        private string QuestionUrl
        {
            get { return _questionUrl; }
        }


        private string Time
        {
            set { TimeLabel.Text = value; }
        }

        private string Title
        {
            set { titleLabel.Text = value; }
        }

        private int Answers
        {
            set
            {
                AnswerValueLabel.Text = value.ToString();

                AnswersBackgroundLabel.BackColor
                    = AnswerValueLabel.BackColor
                      = AnswerCaptionLabel.BackColor
                        = value > 0
                              ? _answersHasAnswersBackgroundColor
                              : _answersHasNoAnswersBackgroundColor;
            }
        }

        private string Views
        {
            set { ViewsLabel.Text = value; }
        }

        private string Votes
        {
            set { VoteCountLabel.Text = value; }
        }

        private bool Accepted
        {
            set
            {
                AnswerValueLabel.ForeColor
                    = AnswerCaptionLabel.ForeColor
                      = value
                            ? _answerAcceptedForeColor
                            : _answerNotAcceptedForeColor;
            }
        }

        /// <summary>
        ///   Tags is a semi-colon delimited list
        /// </summary>
        private string Tags
        {
            set
            {
                if (_tags == value)
                {
                    return;
                }

                _tags = value;

                BeginInvoke(new MethodInvoker(() =>
                                                  {
                                                      TagsLayoutPanel.Controls.Clear();

                                                      var tagsArray = _tags.Split(new[] {';'}, StringSplitOptions.None);
                                                      foreach (string tag in tagsArray)
                                                      {
                                                          var tagLabel = new Label
                                                                             {
                                                                                 AutoSize = true,
                                                                                 BackColor = _tagBackgroundColor,
                                                                                 ForeColor = _tagForeColor,
                                                                                 Text = tag
                                                                             };
                                                          TagsLayoutPanel.Controls.Add(tagLabel);
                                                      }
                                                  }));
            }
        }

        public SoapiDisplay.Location DisplayLocation
        {
            get { return _location; }
        }

        protected void HookUpClickEvents2(Control control)
        {
            if (control != null && control != lockPictureBox)
            {
                control.MouseClick += OnClick;
                foreach (Control c in control.Controls)
                {
                    HookUpClickEvents2(c);
                }
            }
        }


        private void OnClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                Close(true);
            }
            else
            {
                if (sender == titleLabel)
                {
                    Process.Start(QuestionUrl);
                }
                if (!Sticky)
                {
                    OnNotificationClosed(new NotificationCallbackEventArgs(NotificationUUID, CallbackResult.CLOSE));
                    OnClick(e);
                    Close();
                }
            }
        }

        private void SoapiWindowAfterLoad(object sender, EventArgs e)
        {
            DoBeforeShow();
            Tags = _tmpTags;
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            var rect = new Rectangle(0, 0, Width, Height);
            e.Graphics.Clear(ForeColor);
            rect.Inflate(-1, -1);
            Brush b2 = new SolidBrush(BackColor);
            using (b2)
            {
                e.Graphics.FillRectangle(b2, rect);
            }
        }


        public void UpdateUI(Notification notification)
        {
            // (-0|0+|0) title
            try
            {
                _questionUrl = notification.CoalescingID;

                var tRx = new Regex(TitleParsingPattern, RegexOptions.ExplicitCapture);

                var match = tRx.Match(notification.Title);

                Title = match.Groups["title"].Value.Trim();
                Votes = match.Groups["votes"].Value.Trim();
                Views = match.Groups["views"].Value.Trim();
                Answers = Convert.ToInt32(match.Groups["answers"].Value.Trim());
                Accepted = !string.IsNullOrEmpty(match.Groups["accepted"].Value);

                string time = string.Empty;
                string tags = notification.Description;
                int tagStart = tags.IndexOf("[");


                if (tagStart > -1)
                {
                    time = tags.Substring(0, tagStart).Trim();
                    tags = tags.Substring(tagStart);
                }

                // we want to arrive at a semi-colon delimited list of strings
                // as we have tags coming from 2 vectors and need a simple
                // common serialized format that we can compare to avoid rebuilding
                // tags unecessarily

                tags = tags.Replace("[", "").Replace("]", ";").Trim();
                tags = Regex.Replace(tags, @"\s+", "");
                //HACK: to defer tags until window is created.
                _tmpTags = tags;
                Time = time;
            }
            catch
            {
                Title = "This display is designed for use with soapi-notify. Click here for more information";
                _questionUrl = "http://soapi.info";
            }
        }

        public override void SetNotification(Notification notification)
        {
            base.SetNotification(notification);


            Sticky = notification.Sticky;
            SetLockImage();
            if (notification.Duration > 0 && !Sticky)
            {
                SetAutoCloseInterval(notification.Duration*1000);
            }

            Image image = notification.Image;
            if (image != null)
            {
                pictureBox1.Image = image;
                pictureBox1.Visible = true;
            }
            else
            {
                pictureBox1.Visible = false;
            }

            UpdateUI(notification);
        }


        private void SetLockImage()
        {
            lockPictureBox.Image = Sticky ? Resources.lock_on : Resources.lock_off;
        }

        private void LockClick(object sender, EventArgs e)
        {
            Sticky = true;
            SetLockImage();
        }

        public void SetDisplayLocation(SoapiDisplay.Location location)
        {
            _location = location;
        }

        private void DoBeforeShow()
        {
            var d = (MultiMonitorVisualDisplay) Tag;
            Screen screen = d.GetPreferredDisplay();

            _leftXLocation = screen.WorkingArea.Left;
            _rightXLocation = screen.WorkingArea.Right - Width;
            _topYLocation = screen.WorkingArea.Top;
            _bottomYLocation = screen.WorkingArea.Bottom - Height;

            switch (_location)
            {
                case SoapiDisplay.Location.TopLeft:
                    Location = new Point(_leftXLocation, _topYLocation);
                    break;
                case SoapiDisplay.Location.BottomLeft:
                    Location = new Point(_leftXLocation, _bottomYLocation);
                    break;
                case SoapiDisplay.Location.BottomRight:
                    Location = new Point(_rightXLocation, _bottomYLocation);
                    break;
                default: // TopRight
                    Location = new Point(_rightXLocation, _topYLocation);
                    break;
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (pictureBox1 != null) pictureBox1.Dispose();
                if (components != null) components.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}